#-*-encoding:utf-8-*-

import basic_io
import optparse

class FeatureExtractor:

    def __init__(self, network):
        # load network
        self.edges = basic_io.load_network(network)
        # store the vehicles related statistical information
        self.cache = {}
        # store last point second, indexed by vehicle id
        self.buffer = {}
        self.vehicles = {}
        pass
    @staticmethod
    def extract_edge(lane):
        assert type(lane) is str
        return "_".join(lane.split('_')[:-1])

    def process(self, point):
        """
        :param point: ['id', 'time', 'x', 'y', 'angle', 'type', 'speed', 'pos', 'lane']
        :return:
        """
        assert type(point) is list
        # filter some features that will not be used, such as angle, type
        index = [0, 1, 6, 7, 8]
        data = [point[i] for i in index]
        # convert feature lane to edge_id
        data[-1] = FeatureExtractor.extract_edge(data[-1])
        self.stream(data)
        self.buffer_point(data)


    def stream(self, point):
        """
        :param point: ['id', 'time', 'speed', 'pos', 'lane']
        :return:
        """
        assert type(point) is list
        # new inserted vehicle
        if point[0] not in self.buffer:
            return
        # do calculate
        if point[0] not in self.cache:
            self.cache.setdefault(point[0], {})
        temporal = self.cache[point[0]]
        if point[1] - self.buffer[point[0]][0] > 600 or point[1] <= self.buffer[point[0]][0]:
            return
        temporal.setdefault('begin', self.buffer[point[0]][0])
        temporal.setdefault('end', point[1])
        temporal['end'] = int(point[1])
        # distance
        temporal.setdefault('distance', 0)
        #if point[0] == 'hangzhou319':
        #   print "hangzhou319 %s"  % temporal['distance']
        #    print self.buffer[point[0]]
        if point[-1] == self.buffer[point[0]][-1]:
            temporal['distance'] += float(point[-2]) - float(self.buffer[point[0]][-2])
        else:
            temporal['distance'] += float(point[-2]) + float(self.edges[self.buffer[point[0]][-1]]['length']) - float(self.buffer[point[0]][-2])
        #  max speed
        temporal.setdefault('max_speed', 0)
        temporal['max_speed'] = max(temporal['max_speed'], point[-3])
        # accelerate, decelerate
        temporal.setdefault('max_accelerate', 0)
        temporal.setdefault('max_decelerate', 0)
        temporal.setdefault('avg_accelerate', [0, 0])
        temporal.setdefault('avg_decelerate', [0, 0])
        interval = float(point[1]) - float(self.buffer[point[0]][0])
        if point[-1] == self.buffer[point[0]][-1]:
            derivation = (float(point[-3]) - float(self.buffer[point[0]][-3])) / interval
            if derivation < 0:
                temporal['max_decelerate'] = max(temporal['max_decelerate'], - derivation)
                temporal['avg_decelerate'][0] += 1
                temporal['avg_decelerate'][1] -= derivation
            else:
                temporal['max_accelerate'] = max(temporal['max_accelerate'], derivation)
                temporal['avg_accelerate'][0] += 1
                temporal['avg_accelerate'][1] += derivation
        # number of intersection
        temporal.setdefault('intersection', 0)
        if point[-1] != self.buffer[point[0]][-1]:
            temporal['intersection'] += 1
        self.cache[point[0]] = temporal


    def buffer_point(self, point):
        """
        :param point: ['id', 'time', 'speed', 'pos', 'lane']
        :return:
        """
        assert type(point) is list
        self.buffer.setdefault(point[0], [])
        self.buffer[point[0]] = point[1:]

    def extract_feature(self):
        for vid in self.cache:
            temporal = self.cache[vid]
            self.vehicles.setdefault(vid, {})
            self.vehicles[vid].setdefault('driving_time', temporal['end'] - temporal['begin'])
            self.vehicles[vid].setdefault('driving_distance', temporal['distance'])
            self.vehicles[vid].setdefault('max_speed', temporal['max_speed'])
            self.vehicles[vid].setdefault('avg_speed', temporal['distance'] / (temporal['end'] - temporal['begin']))
            self.vehicles[vid].setdefault('max_decelerate', temporal['max_decelerate'])
            self.vehicles[vid].setdefault('max_accelerate', temporal['max_accelerate'])
            avg_decelerate = 0
            if  temporal['avg_decelerate'][0] != 0:
                avg_decelerate = temporal['avg_decelerate'][1] / temporal['avg_decelerate'][0]
            self.vehicles[vid].setdefault('avg_decelerate', avg_decelerate)
            avg_accelerate = 0
            if temporal['avg_accelerate'][0] != 0:
                avg_accelerate = temporal['avg_accelerate'][1] / temporal['avg_accelerate'][0]
            self.vehicles[vid].setdefault('avg_accelerate', avg_accelerate)
            self.vehicles[vid].setdefault('intersection', temporal['intersection'])

    def write_feature(self, fname):
        keyset = ['driving_time', 'driving_distance', 'max_speed', 'avg_speed', 'max_decelerate', 'max_accelerate', 'avg_decelerate', 'avg_accelerate', 'intersection']
        with open(fname, "wb") as f:
            f.write('id,{0}\n'.format(",".join(keyset)))
            for vid in self.vehicles:
                line = vid
                for key in keyset:
                    line += ",%s" % (self.vehicles[vid][key])
                f.write("{0}\n".format(line))
        print "write feature successful"

def get_options(args=None):
    parser = optparse.OptionParser()
    parser.add_option("-n", "--network", dest="network", help="road network")
    parser.add_option("-g", "--gps", dest="gps", help="gps trajectories in csv format")
    parser.add_option("-o", "--output", dest="output", help="output feature")
    (options, args) = parser.parse_args(args=args)
    if not options.network or not options.gps or not options.output:
        parser.print_help()
        exit(1)
    return options


def main(options=None):
    extractor = FeatureExtractor(options.network)
    basic_io.load_gps_csv(options.gps, extractor)
    extractor.extract_feature()
    extractor.write_feature(options.output)

if __name__ == '__main__':
    main(get_options())
